#include "../headers/bib_Comunicacion.h"

static uint32_t generarId(void);


extern char *buffer;
extern char *indiceBuffer;	/*0 bloque libre, 1 bloque ocupado*/
extern sem_t semBuffer;
extern pthread_mutex_t mutexIndice;

extern bsUtil bsu;
extern CONFIG_EST config;

extern uint32_t tamBuffer;

uint32_t proximoIdPlanif = SOLICITUD_MIN;
sem_t accesoIdPlanif;


static uint32_t generarId(void){

	uint32_t valor;

	//Solicitar semaforo
	sem_wait(&accesoIdPlanif);

	valor=proximoIdPlanif;

	if (proximoIdPlanif==SOLICITUD_MAX)
		proximoIdPlanif=SOLICITUD_MIN;
	else
		proximoIdPlanif++;

	//Liberar semáforo
	sem_post(&accesoIdPlanif);

	return valor;
}
/*=====================================================================================*/
int inicializarSemaforos(void){

	if (sem_init(&accesoIdPlanif, 0, 1))
		return FIN_ERROR;

	return FIN_OK;
}
/*=====================================================================================*/
int crearBuffer(void){

	char i;

	tamBuffer = TAM_BLOQUE * TAM_CLUSTER * TAM_SECTOR * config.cantConexMax * sizeof(char);

	if (!(buffer = (char *) malloc(tamBuffer))){
		imprimirLog(ERROR, "No se pudo reservar la memoria para el buffer.", strerror(errno));
		return FIN_ERROR;
	}
	
	if (!(indiceBuffer = (char *) malloc(sizeof(char) * config.cantConexMax))){
		imprimirLog(ERROR, "No se pudo reservar la memoria para el indice del buffer.", strerror(errno));
		free(buffer);
		return FIN_ERROR;
	}
	
	for (i = 0; i < config.cantConexMax; i++)
		*(indiceBuffer + i) = 0;
		
	if (sem_init(&semBuffer, 0, config.cantConexMax) == -1){
		imprimirLog(ERROR, "No se pudo inicializar el semaforo del buffer.", strerror(errno));
		free(buffer);
		free(indiceBuffer);
		return FIN_ERROR;
	}
	
	if (pthread_mutex_init(&mutexIndice, NULL)){
		imprimirLog(ERROR, "No se pudo crear el mutex para acceder al indice del buffer", "");
		free(buffer);
		free(indiceBuffer);
		sem_destroy(&semBuffer);
		return FIN_ERROR;
	}

	return FIN_OK;
}
/*=====================================================================================*/
int liberarBuffer(void){

	free(buffer);
	free(indiceBuffer);
	sem_destroy(&semBuffer);
	pthread_mutex_destroy(&mutexIndice);
	
	return FIN_OK;
}
/*=====================================================================================*/
int obtenerIdBloqueLibre(char *idBloque){

	char i = 0;
	
	sem_wait(&semBuffer);
	pthread_mutex_lock(&mutexIndice);

	while(*(indiceBuffer + i))
		i++;
		
	*(indiceBuffer + i) = 1;
	pthread_mutex_unlock(&mutexIndice);
	
	*idBloque = i;
	
	return FIN_OK;
}
/*=====================================================================================*/
int liberarIdBuffer(char idBloque){

	pthread_mutex_lock(&mutexIndice);

	*(indiceBuffer + idBloque) = 0;

	pthread_mutex_unlock(&mutexIndice);

	sem_post(&semBuffer);

	return FIN_OK;
}
/*=====================================================================================*/
int conecPRAID(int *sockPRAID){
	
	struct HEADER_NIPC header;
	char *payload;

	abrirSocketCliente(sockPRAID, config.puerto, config.ip);

	header.type = TIPO_HANDSHAKE;
	header.payloadLength = 0;

	if (enviarMensajeNIPC(*sockPRAID, &header, NULL))
		return FIN_ERROR;

	if (recibirMensajeNIPCDinamico(*sockPRAID, &header, &payload))
		return FIN_ERROR;

	if ((header.type != TIPO_HANDSHAKE) || (header.payloadLength)){
		imprimirLog(ERROR, "Fallo en el Handshake", "");
		if (payload){
				imprimirLog(ERROR, "", payload);
				free(payload);
			}
		return FIN_ERROR;
	}

	return FIN_OK;
}
/*=====================================================================================*/
int desconecPRAID(int sockPRAID){

	cerrarSocket(sockPRAID);

	return FIN_OK;
}
/*=====================================================================================*/
int getBloque(int num, char **bufBloque, char *idBloque){

	obtenerIdBloqueLibre(idBloque);
	*bufBloque = buffer + (*idBloque) * TAM_SECTOR;

	getCluster(num, *bufBloque);

	return FIN_OK;
}
/*=====================================================================================*/
int getCluster(int num, char *bufCluster){

	int i;
	uint32_t secIni = bsu.reservedSectorCount + bsu.numberOfFats * bsu.sectorsPerFat + (num - 2) * bsu.sectorsPerCluster;

	for(i = 0; i < TAM_CLUSTER; i++)
		getSector(secIni + i, bufCluster + i * TAM_SECTOR);

    return FIN_OK;
}
/*=====================================================================================*/
int getSector(int num, char *bufSector){

	int sock;
	struct HEADER_NIPC header;
	char *payload;
	struct PAYLOAD_LECTURA payloadLect;
	struct PAYLOAD_RESPUESTA_LECTURA respuestaLect;
	
	conecPRAID(&sock);
	
	header.type=LECTURA;
	header.payloadLength=sizeof(struct PAYLOAD_LECTURA);
	
	payloadLect.id = generarId();	////Desarrollar funcion
	payloadLect.sector = num;
	
	if (enviarMensajeNIPC(sock, &header, (char *) &payloadLect)){
		return FIN_ERROR;
	}
	
	if (recibirMensajeNIPCDinamico(sock,&header,&payload)){
		return FIN_ERROR;
	}
	
	desconecPRAID(sock);
	
	memcpy(&respuestaLect,payload,sizeof(struct PAYLOAD_RESPUESTA_LECTURA));
	free(payload);
	
	memcpy(bufSector,respuestaLect.buffer,TAM_SECTOR * sizeof(char));

	return FIN_OK;
}
/*=====================================================================================*/
int setBloque(int num, char idBloque){

	char *bufBloque;

	bufBloque = buffer + idBloque * TAM_CLUSTER * TAM_SECTOR;

	setCluster(num, bufBloque);
	
	liberarIdBuffer(idBloque);

	return FIN_OK;
}
/*=====================================================================================*/
int setCluster(int num, char *bufCluster){

	int i;
	uint32_t secIni = bsu.reservedSectorCount + bsu.numberOfFats * bsu.sectorsPerFat + (num - 2) * bsu.sectorsPerCluster;

	for(i = 0; i < TAM_CLUSTER; i++)
		setSector(secIni + i, bufCluster + i * TAM_SECTOR);

    return FIN_OK;
}
/*=====================================================================================*/
int setSector(int num, char *bufSector){

	int sock;
	struct PAYLOAD_ESCRITURA payloadEscritura;
	char *payload;
	struct HEADER_NIPC header;
	
	conecPRAID(&sock);
	
	header.type=ESCRITURA;
	header.payloadLength=sizeof(struct PAYLOAD_LECTURA);
	
	payloadEscritura.id = generarId();
	payloadEscritura.sector = num;
	memcpy(payloadEscritura.buffer, bufSector, TAM_SECTOR * sizeof(char));
	
	if (enviarMensajeNIPC(sock, &header, (char *) &payloadEscritura)){
		return FIN_ERROR;
	}
	
	if (recibirMensajeNIPCDinamico(sock, &header, &payload)){
		return FIN_ERROR;
	}
	
	desconecPRAID(sock);
	
	free(payload);
	
	return FIN_OK;
}
